package com.conversationboard.controller;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.collections.map.LRUMap;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.ee.servlet.QuartzInitializerServlet;
import org.quartz.impl.StdSchedulerFactory;
import org.xml.sax.SAXException;

import com.conversationboard.batch.jobs.stats.CountersJob;
import com.conversationboard.batch.scheduler.Jobs;
import com.conversationboard.cache.ThreadTitleCache;
import com.conversationboard.cache.VoterCache;
import com.conversationboard.config.Configuration;
import com.conversationboard.logger.LogMessageType;
import com.conversationboard.logger.Logger;
import com.conversationboard.model.Board;
import com.conversationboard.model.Boards;
import com.conversationboard.model.SiteMessage;
import com.conversationboard.model.StyleSheets;
import com.conversationboard.model.adminlog.AdminLog;
import com.conversationboard.model.propertybag.Property;
import com.conversationboard.model.propertybag.PropertyBag;
import com.conversationboard.serialisation.XmlSerialiser;
import com.conversationboard.site.AutoNumber;
import com.conversationboard.site.Site;
import com.conversationboard.tree.Folders;

@WebServlet(name = "StartupControllerServlet", loadOnStartup = 2)
public class StartupControllerServlet extends HttpServlet {

	private static final long serialVersionUID = 4854856393929525708L;


	public static void loadConfiguration(ServletContext context) throws XPathExpressionException, IOException, ParserConfigurationException, SAXException, SQLException {

		String configurationFilePath = context.getRealPath("/Config/");
		Configuration.getInstance().setConfigurationFilePath(configurationFilePath);

		Configuration.load();

		String stylesheetsPath = context.getRealPath("/Stylesheets/");
		StyleSheets.setStylesheetsPath(stylesheetsPath);

	}


	public void init() throws ServletException {

		try {

			/* 1. Load the configuration */

			loadConfiguration(this.getServletContext());

			/* Point standard out and errors to the logger */

			// System.setOut(Logger.getOutput());
			// System.setErr(Logger.getOutput());

			/* 1.1 Set the configuration root so we can access it anywhere */

			Configuration.getInstance().setRoot(this.getServletContext().getContextPath());

			Logger.log(new Date(), "<span class='green'><strong>Beginning startup sequence...</strong></span>", null, LogMessageType.SERVER);

			/* 1.2 Shut the site, unless it's configured to be open on startup */

			if (!Configuration.getInstance().isSiteOpenOnStartup()) {
				Site.setShut(true);
			} else {
				Site.setShut(false);
			}

			/* Temporary - set the site open */

			Site.setShut(false);

			/* 2: Get the maximum values for the keys; do it lazily if it's a test server */

			if (!Configuration.getInstance().isTestServer()) {
				SiteMessage.setMessage("Attempting to load the maximum values for keys. If this message is still showing when the site is up, stop the site immediately and investigate.");
				AutoNumber.initialiseAutoNumbers();
			}

			/* 3. If the voter cache exists, load it */

			if (!Configuration.getInstance().isTestServer()) {
				Logger.log(new Date(), "Loading Voter Cache...", null, LogMessageType.SERVER);

				Property property = PropertyBag.get("votercache");

				if (property != null) {

					String xml = property.getValue();

					XmlSerialiser<LRUMap> serialiser = new XmlSerialiser<LRUMap>();
					LRUMap voterCache = serialiser.deSerialise(xml);

					VoterCache.setVoterCache(voterCache);
				}
			}

			Logger.log(new Date(), "Loaded Voter Cache...", null, LogMessageType.SERVER);

			/* 4. Load all the new answers caches up to avoid any race conditions with people using the site.
			 * Don't do this if it's a test server. */

			if (!Configuration.getInstance().isTestServer()) {

				Logger.log(new Date(), "Loading New Answers Caches...", null, LogMessageType.SERVER);

				List<Board> boards = Boards.getAllBoards();

				for (Board board : boards) {
					board.getThreadsWithNewAnswers();
				}

				Site.initialiseSiteNewAnswersCache();

				Logger.log(new Date(), "Loaded all boards' new answers caches...", null, LogMessageType.SERVER);
			}

			/* 5. Load the thread title cache  - don't think I can get away with not doing this. */

			Logger.log(new Date(), "Loading Thread Title Cache...", null, LogMessageType.SERVER);
			ThreadTitleCache.loadAllThreadTitles();

			/* 6. Start the job scheduler */

			Logger.log(new Date(), "Starting Job Scheduler...", null, LogMessageType.SERVER);

			ServletContext context = this.getServletContext();

			StdSchedulerFactory factory = (StdSchedulerFactory) context.getAttribute(QuartzInitializerServlet.QUARTZ_FACTORY_KEY);
			Scheduler scheduler = factory.getScheduler();

			Jobs.initialise(scheduler);

			Logger.log(new Date(), "Started batch job scheduler. Startup complete. <b>Ensure the domain name is set properly by opening the site</b>", null, LogMessageType.SERVER);
			Logger.log(new Date(), "***** Startup complete *****", null, LogMessageType.SERVER);

			/* 6: Initialise Admin Log */

			AdminLog.getInstance();

			/* 7: Initialise Folders */

			Folders.getInstance();

			/* 8: Initialise Counters, unless it's a test server */

			if (!Configuration.getInstance().isTestServer()) {
				CountersJob countersJob = new CountersJob();
				countersJob.initialiseCounters();
			}

			/* 9: Set the Threads/Posts */

			if (Configuration.getInstance().isThreadsPosts()) {
				Site.setThread("Thread");
				Site.setPost("Post");
			} else {
				Site.setThread("Question");
				Site.setPost("Answer");
			}

		} catch (JobExecutionException e) {
			throw new ServletException(e);
		} catch (SchedulerException e) {
			e.printStackTrace();
			throw new ServletException(e);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			throw new ServletException(e);
		} catch (XPathExpressionException e) {
			e.printStackTrace();
			throw new ServletException(e);
		} catch (SAXException e) {
			e.printStackTrace();
			throw new ServletException(e);
		} catch (SQLException e) {
			e.printStackTrace();
			throw new ServletException(e);
		} catch (IOException e) {
			e.printStackTrace();
			throw new ServletException(e);
		}
	}

}
